#include "../include/kasm.h"
#include "../include/defs.h"
#include "../include/kc.h"
#include "../include/memoria.h"
#include "../src/shell.c"
#include "../include/malloc.h"
#include "../include/libc.h"
#include "../include/output.h"

/* Check if the bit BIT in FLAGS is set. */
#define CHECK_FLAG(flags,bit)   ((flags) & (1 << (bit)))
#define MAX(a,b) ((a)>(b))?(a):(b)

#define FD_TECLADO 0
#define FD_PCI 1
#define FD_PIC 2

int tickpos=640;
unsigned char respuesta;

/* IDT de 256 entradas */

DESCR_INT idt[256];	
IDTR idtr;
PROCESS idle;
PROCESS procesos[64];
int CurrentPID=0;
static int nextPID=1;				

TTY tty[8];

/****************************************************************************/

void
eoi_pic()
{
	Tparametros parametros;

	unsigned char pic = 0x20;

	parametros.fd = FD_PIC;
	parametros.buffer = &pic;
	parametros.longit = 1;
	parametros.opcion = 0;
	
	_write(&parametros,0);

	return;
}

/****************************************************************************/

void int_08() {

    tickpos++;

}

/****************************************************************************/

void int_09() {

	Tparametros parametros;

	parametros.fd=FD_TECLADO;
	parametros.buffer=&respuesta;
	parametros.longit=1;
	parametros.opcion=1;

	_read(&parametros);

	if(keyboard==0)
		teclado(*(parametros.buffer),posicion);

	else if(keyboard==1)
		tecladoesp(*(parametros.buffer),posicion);

	return;
}

/****************************************************************************/

void
int_80(Tparametros * parametros, int offset)
{
	if(parametros->opcion==1)
		switch(parametros->fd)
		{
			case 0:
				(*(parametros->buffer))=_leer_teclado();
				break;
			case 1:
				/*(*respuesta)=_leer_pci();*/
				break;
		}

	if(parametros->opcion==0)
	{
		switch(parametros->fd)
		{
			case 0:
				escribir_pantalla(*parametros, offset);
				break;
			case 1:
				/*_escribir_pci();*/
				break;
			case 2:
				_escribo_pic(*(parametros->buffer));
		}
	}

	return;
}

int Test(int argc, char* argv[]){
  return 0;
}
//Retorna un proceso a partir de su nombre
PROCESS* GetProcessByName (char* proceso)
{
	int i, longitud;
	
	if (memcmp(proceso, "Idle", 4))
	{
		return &idle;
	}
	longitud = strlen(proceso);
	for(i=0;i<64;i++)
	{
		if (memcmp(proceso, procesos[i].name, MAX(longitud, strlen(procesos[i].name))))
		{
			return &procesos[i];
		}
	}
	return 0;
}

//Retorna un proceso a partir de su PID
PROCESS* GetProcessByPID (int pid)
{
	int i;
	
	if (pid==0)
	{
		return &idle;
	}
	for(i=0;i<64;i++)
	{
		if (procesos[i].pid== pid)
		{
			return &procesos[i];
		}
	}
	return 0;
}

//Retorna la TTY del proceso que se esta ejecutando
int GetTTY()
{
	PROCESS* temp;
	temp=GetProcessByPID(CurrentPID);
	return temp->tty;
}

//Retorna un PID no utilizado
int GetPID(void)
{
	return nextPID++;
}

//Funcion que destruye un proceso a partir de su PID
void Destroy(int PID)
{
	PROCESS* proc;
	PROCESS* padre;
	int test;
	char temp[10];
	char temp2[100];
	itoa2(PID,temp);
	proc=GetProcessByPID(PID);
	if (!proc->free)
	{
		memcpy2(temp2,"Killed->",8);
		test=strlen2(proc->name)+8;
		memcpy2(&temp2[8],proc->name,test-8);
		temp2[test++]='\0';
		puterr(temp2);
		tty[proc->tty].buffer.head=0;
		tty[proc->tty].buffer.tail=0;
		if (proc->parent!=0)
		{
			padre=GetProcessByPID(proc->parent);
			padre->blocked=0;	
		}
		proc->free = 1;
	}
	else
		puterr("Proceso Inexistente");
}

//Funcion a donde van a parar las funciones que terminan.
void Cleaner(void)
{
	_Cli();
	Destroy(CurrentPID);
	k_clear_screen();
	mess("==>");
	_Sti();
	while(1);
	
}
//Funcion que arma los stack frames de cada proceso
int LoadStackFrame(int(*process)(int,char**),int argc,char** argv, int bottom, void(*cleaner)())
{
	STACK_FRAME* frame= (STACK_FRAME*)(bottom-sizeof(STACK_FRAME));
	frame->EBP=0;
	frame->EIP=(int)process;
	frame->CS=0x08;
	
	frame->EFLAGS=0;
	frame->retaddr=cleaner;
	frame->argc=argc;
	frame->argv=argv;
	return (int)frame;
}

//Funcion que crea procesos
void CreateProcessAt(char* name, int (*process)(int,char**),int tty, int argc, char** argv, int stacklength, int priority, int isFront)
{
	int i;
	void* stack=Malloc(stacklength);
	for(i=0;i<64;i++)
	{
		if(procesos[i].free==1)
			break;
	}
	procesos[i].pid=GetPID();
	procesos[i].foreground=isFront;
	procesos[i].priority=priority;
	memcpy2(procesos[i].name,name,strlen(name)+1);
	procesos[i].sleep=0;
	procesos[i].blocked=0;
	procesos[i].tty=tty;
	procesos[i].lastCalled=0;
//	procesos[i].stacksize=stacklength;
	procesos[i].stackstart=(int)stack;
	procesos[i].free=0;
	procesos[i].ESP=LoadStackFrame(process,argc,argv,(int)(stack+stacklength-1),Cleaner);
	procesos[i].parent=0;
	if(isFront && CurrentPID>=1)
	{
		PROCESS* proc=GetProcessByPID(CurrentPID);
		proc->blocked=2;
		procesos[i].parent=CurrentPID;
	}
	
}


/****************************************************************************/
/*  kmain(): Punto de entrada del codigo C
/****************************************************************************/

kmain() 
{
	/* Carga la IDT con las rutinas de atencion a las interrupciones */

        setup_IDT_entry (&idt[0x08], 0x08, (dword)&_int_08_hand, ACS_INT, 0);
	setup_IDT_entry (&idt[0x09], 0x08, (dword)&_int_09_hand, ACS_INT, 0);	
	setup_IDT_entry (&idt[0x80], 0x08, (dword)&_int_80_hand, ACS_INT, 0);

	/* Carga de IDTR    */

	idtr.base = 0;  
	idtr.base +=(dword) &idt;
	idtr.limit = sizeof(idt)-1;
	
	_lidt (&idtr);	

	_Cli();

	/* Habilito las interrupciones */

        _mascaraPIC1(0xFC);
        _mascaraPIC2(0xFF);
        
	/* Se mapea la memoria y se habilita la paginacion. */
	init_paging ();

	CreateProcessAt("Shell 0",0,0,0,(char**)0,4096,2,0);
//	CreateProcessAt("Shell 1",0,1,0,(char**)0,0x1000,2,1);
//	CreateProcessAt("Shell 2",0,2,0,(char**)0,0x400,2,1);
//	CreateProcessAt("Shell 3",0,3,0,(char**)0,0x400,2,1);

	_Sti();

	

        shell();
}

